package streamforce.crypto;

public class KPAbe {
	static {
		System.loadLibrary("proxyKPAbe");
	}

	// data owner API. Assume one sliding window size per owner.
	public  native byte[] initOwner(int ownerId, String[] attributeUniverse, int nColumns, int[] windowSizes);

	public  native byte[] enc_det(int streamId, int value);
	
	public  native byte[] enc_simple(int streamId, String[] attributes, int value, int index);

	// index of the record in the stream 0,1,2...
	public  native byte[] enc_slidingWindow_Aggregate1(int streamId, int columnId, int windowSize, String[] attributes, int value,
			int index);

	public  native byte[] enc_slidingWindow_Aggregate3(int streamId, int columnId, String[] attributes1, String[] attributes2, int value, int index, int[] lens);
	public  native byte[] extractOriginalCipher(int streamId, byte[] cipher);

	// the TK used by the cloud
	public  native byte[] generateTransformKey(int streamId, String policy, int userId);

	// zu
	public  native byte[] generateUserDecryptionKey(int streamId, int userId);

	// SK for aggregate-1
	public  native byte[] generateUserWindowKey_Aggregate1(int streamId, int windowSize);

	// use for deterministic encryption, JOIN
	public  native byte[] generateUserDetKey(int streamId);

	// data user API.
	public  native void initUser_simple(int userId, int columnId, byte[] pubK, byte[] tK, byte[] decK, byte[] detK);

	public  native void initUser_slidingWindow_Aggregate1(int userId, byte[] pubK, byte[] tK,
			byte[] decK, int windowSize, byte[] windowKey);

	// pre-compute discrete-log solution: g^x -> x
	public  native void initDiscreteLogMap(int max);

	// return -1 if invalid decryption, plaintext value otherwise
	public native int decrypt_simple(int userId, byte[] transformedCipher,
			byte[] originalCipher);

	public  native int decrypt_slidingWindow_Aggregate1(int userId, byte[] transformedCipher,
			byte[] originalCipher);

	public  native int decrypt_slidingWindow_Aggregate3(int userId, int rUserId, int columnId, byte[] transformedCipher, byte[] originalCipher, byte[] rTransformedCipher,
byte[] rOriginalCipher);

	//byte array containing two keys of equal lengths, need to be split
	public  native byte[] getJoiningKey(int userId1, int userId2);

	// cloud API. Each public parameter is mapped to a streamId, each user a
	// policyId
	public  synchronized native void initACInstance(byte[] pubK, int streamId,
			byte[] transformK, int userId, byte[] detK);

	public native byte[] transform_simple(byte[] ciphertext, int streamId,
			int userId);
	
	public  native byte[] transform_join(int streamId, int userId, byte[] ciphertext);

	public  native byte[] multiplyCiphers(byte[][] ciphertexts, int streamId);

	//cipher1/cipher2
	public  native byte[] divideCiphers(byte[] cipher1, byte[] cipher2, int streamId);

	public  native String bytesToString(byte[] bytes);

	public static void main(String[] argv) {
		//testJoining();
		testAggregate1();
		//testAggregate2();
		//testAggregate3();
	}

	public static void testAggregate2(){
		int[] windowSizes = {2,3};
		String[] universe = new String[2];
		universe[0] = "x";
		universe[1] = "index = ";
		
		String[] atts_w2 = new String[2];
		atts_w2[0] = "x";
		atts_w2[1] = "index = 23";
	
	
		KPAbe abeHandler = new KPAbe();
		byte[] pubK = abeHandler.initOwner(0,universe,1,windowSizes);
		byte[] userDecK = abeHandler.generateUserDecryptionKey(0,0);
		byte[] userDecK1 = abeHandler.generateUserDecryptionKey(0,1);
		byte[] userDetK = abeHandler.generateUserDetKey(0);
		byte[] tK = abeHandler.generateTransformKey(0,"x",0);
		byte[] tK1 = abeHandler.generateTransformKey(0,"(index % 8 = 1)",1);		
		

		abeHandler.initUser_simple(0,0,pubK,tK,userDecK,userDetK);
		abeHandler.initUser_simple(1,0,pubK,tK1,userDecK1,userDetK); 	
	
		abeHandler.initACInstance(pubK,0,tK,0,userDetK);
		abeHandler.initACInstance(pubK,0,tK1,1,userDetK);
	
		//encrpt with windowSize = 2
		byte[] cipher = abeHandler.enc_simple(0,atts_w2,13,1);	
		byte[] originalCipher = abeHandler.extractOriginalCipher(0,cipher);
		byte[] transformedCipher = abeHandler.transform_simple(cipher,0,0);
		byte[] transformedCipher1 = abeHandler.transform_simple(cipher,0,1);
		
		System.out.println("Decrypted value, (ws=2): "+abeHandler.decrypt_simple(0,transformedCipher, originalCipher));	
		System.out.println("Decrypted value, (ws=3): "+abeHandler.decrypt_simple(1,transformedCipher1, originalCipher));	

	}

	public static void testAggregate3(){
		int[] windowSizes = {2,3};
		String[] universe = new String[2];
		universe[0] = "x";
		universe[1] = "index = ";
		
		KPAbe abeHandler = new KPAbe();
		byte[] pubK = abeHandler.initOwner(0,universe,1,windowSizes);
		byte[] userDecK1 = abeHandler.generateUserDecryptionKey(0,0);
		byte[] userDecK2 = abeHandler.generateUserDecryptionKey(0,1);
		byte[] userDetK = abeHandler.generateUserDetKey(0);
		byte[] tK1 = abeHandler.generateTransformKey(0,"x",0);		
		byte[] tK2 = abeHandler.generateTransformKey(0,"(index % 4 = 3)",1);
	
		abeHandler.initUser_simple(0,0,pubK,tK1,userDecK1,userDetK);
		abeHandler.initUser_simple(1,0,pubK,tK2,userDecK2,userDetK);
		abeHandler.initACInstance(pubK,0,tK1,0,userDetK);
		abeHandler.initACInstance(pubK,0,tK2,1,userDetK);
	
		String[] atts1 = new String[1];
		atts1[0] = "x";
		String[] atts2 = new String[1];
		atts2[0] = "index = 0";	
		int[] lens = new int[2];
		byte[] cipher0 = abeHandler.enc_slidingWindow_Aggregate3(0,0, atts1,atts2,0,0, lens);
		byte[] c00 = new byte[lens[0]];
		System.arraycopy(cipher0,0,c00,0,lens[0]);
		byte[] e0 = abeHandler.extractOriginalCipher(0,c00);
		byte[] tc0 = abeHandler.transform_simple(c00,0,0);
		byte[][] po = new byte[4][e0.length];
		po[0] = e0;
		byte[][] pt = new byte[4][tc0.length];
		pt[0] = tc0; 
		byte[][] ciphers = new byte[4][cipher0.length];
		ciphers[0] = cipher0; 
		byte[] tmp;
		byte[] prodOrin, prodTrans,rt,ro;
		for (int i=1; i<500; i++){
			atts2[0] = "index = "+i;
			//encrypt
			ciphers[i%4] = abeHandler.enc_slidingWindow_Aggregate3(0,0,atts1,atts2,2*i,i, lens);
			tmp = new byte[lens[0]];
			System.arraycopy(ciphers[i%4],0,tmp,0,lens[0]);
			po[i%4] = abeHandler.extractOriginalCipher(0,tmp);
			pt[i%4] = abeHandler.transform_simple(tmp,0,0);
			if (i%4==3){
				prodOrin = abeHandler.multiplyCiphers(po,0);
				prodTrans = abeHandler.multiplyCiphers(pt,0);
				tmp = new byte[lens[1]];
				System.arraycopy(ciphers[3],lens[0],tmp,0,lens[1]);
				rt = abeHandler.transform_simple(tmp,0,1);
				ro = abeHandler.extractOriginalCipher(0,tmp);
				System.out.println("value = "+abeHandler.decrypt_slidingWindow_Aggregate3(0,1,0,prodTrans,prodOrin,rt,ro));	
			}
		}
		
		/*	
		byte[] c00 = new byte[lens[0]];
		byte[] c10 = new byte[lens[0]];
		byte[] c20 = new byte[lens[0]];
		byte[] c30 = new byte[lens[0]]; byte[] c31 = new byte[lens[1]];
		System.arraycopy(cipher0,0,c00,0,lens[0]);
		System.arraycopy(cipher1,0,c10,0,lens[0]);
		System.arraycopy(cipher2,0,c20,0,lens[0]);
		System.arraycopy(cipher3,0,c30,0,lens[0]);
		System.arraycopy(cipher3,lens[0],c31,0,lens[1]);
		
		byte[] e0 = abeHandler.extractOriginalCipher(0,c00);
		byte[] e1 = abeHandler.extractOriginalCipher(0,c10);
		byte[] e2 = abeHandler.extractOriginalCipher(0,c20);
		byte[] e3 = abeHandler.extractOriginalCipher(0,c30);
		byte[][] po = new byte[4][e0.length];
		po[0] = e0; po[1] = e1; po[2] = e2; po[3] = e3;
		byte[] prodOrin = abeHandler.multiplyCiphers(po,0);

		byte[] tc0 = abeHandler.transform_simple(c00,0,0);
		byte[] tc1 = abeHandler.transform_simple(c10,0,0);
		byte[] tc2 = abeHandler.transform_simple(c20,0,0);
		byte[] tc3 = abeHandler.transform_simple(c30,0,0);
		byte [][] pt = new byte[4][tc0.length];
		pt[0] = tc0; pt[1] = tc1; pt[2] = tc2; pt[3] = tc3;
		byte[] prodTrans = abeHandler.multiplyCiphers(pt,0);
	
		byte[] rt = abeHandler.transform_simple(c31,0,1);
		byte[] ro = abeHandler.extractOriginalCipher(0,c31);
	
		System.out.println("value = "+abeHandler.decrypt_slidingWindow_Aggregate3(0,1, prodTrans,prodOrin,rt,ro));	
		}*/
	}

	public static void testAggregate1(){
		int[] windowSizes = {3,5};
		String[] universe = new String[1];
		universe[0] = "x";

		String[] atts = new String[1];
		atts[0] = "x";
		KPAbe abeHandler = new KPAbe();

		//owner initialization
		byte[] pubK = abeHandler.initOwner(0,universe, 1, windowSizes);
		byte[] userK = abeHandler.generateUserDecryptionKey(0,0);
		byte[] userDet = abeHandler.generateUserDetKey(0);
		byte[] tK = abeHandler.generateTransformKey(0,"x", 0);
		byte[] wK = abeHandler.generateUserWindowKey_Aggregate1(0,3); 	
		
		abeHandler = new KPAbe();
		//cloud initialization
		abeHandler.initACInstance(pubK,0,tK,0,userDet);
	
		//encrypt 2 values
		byte[] cipher0 = abeHandler.enc_slidingWindow_Aggregate1(0,1,3,atts,13,0);
		byte[] cipher1 = abeHandler.enc_slidingWindow_Aggregate1(0,1,3,atts,17,1);
		byte[] cipher2 = abeHandler.enc_slidingWindow_Aggregate1(0,1,3,atts,18,2); 
		byte[] e0 = abeHandler.extractOriginalCipher(0,cipher0);
		byte[] e1 = abeHandler.extractOriginalCipher(0,cipher1);
		byte[] e2 = abeHandler.extractOriginalCipher(0,cipher2);
		byte[][] po = new byte[3][e0.length];
		po[0] = e0;
		po[1] = e1;
		po[2] = e2;
		byte[] productOriginal = abeHandler.multiplyCiphers(po,0);	
		System.out.println("original length = "+productOriginal.length);
	
		byte[] tc0 = abeHandler.transform_simple(cipher0,0,0);
		byte[] tc1 = abeHandler.transform_simple(cipher1,0,0);
		byte[] tc2 = abeHandler.transform_simple(cipher2,0,0);
		byte[][] pt = new byte[3][tc0.length];
		pt[0] = tc0;
		pt[1] = tc1;
		pt[2] = tc2;
		byte[] productTransform = abeHandler.multiplyCiphers(pt,0);
		System.out.println("transformed length = "+productTransform.length);		
		
		//user initialization
		abeHandler  = new KPAbe();
		abeHandler.initUser_slidingWindow_Aggregate1(0,pubK, tK, userK, 3, wK);

		System.out.println("Value = "+abeHandler.decrypt_slidingWindow_Aggregate1(0,productTransform,productOriginal));	
	}

	public static void testJoining(){
		int[] windowSizes = {2,3};
		String[] universe = new String[1];
		universe[0] = "x";

		String[] atts = new String[1];
		atts[0] = "x";
		KPAbe abeHandler = new KPAbe();
		long overallStart = System.currentTimeMillis();

		byte[] pubK = abeHandler.initOwner(0,universe, 1, windowSizes);

		byte[] userK = abeHandler.generateUserDecryptionKey(0,0);
		//byte[] mask = abeHandler.generateUserMaskValue(0,0);
		byte[] userDet = abeHandler.generateUserDetKey(0);
		byte[] tK = abeHandler.generateTransformKey(0,"x", 0);
		
		abeHandler.initUser_simple(0,0,pubK,tK,userK,userDet);

		KPAbe abeHandler1 = new KPAbe();
		byte[] pubK1 = abeHandler1.initOwner(1,universe,1, windowSizes);
		byte[] userK1 = abeHandler1.generateUserDecryptionKey(1,0);
		//byte[] mask1 = abeHandler1.generateUserMaskValue(1,0);
		byte[] userDet1 = abeHandler1.generateUserDetKey(1);
		byte[] tk1 = abeHandler1.generateTransformKey(1,"x",0);
		byte[] aesCipher1 = abeHandler1.enc_det(1,70);
		byte[] aesCipher = abeHandler.enc_det(0,70);

		abeHandler.initUser_simple(1,0,pubK1,tk1,userK1,userDet1);
	
		byte[] joiningKeys = abeHandler.getJoiningKey(0,1);	
		System.out.println("User done computing joining keys, length ="+joiningKeys.length);
		byte[] jK1 = new byte[joiningKeys.length/2];
		byte[] jK2 = new byte[joiningKeys.length/2];
		System.arraycopy(joiningKeys,0,jK1,0,joiningKeys.length/2);
		System.arraycopy(joiningKeys,joiningKeys.length/2,jK2,0,joiningKeys.length/2);
		
		abeHandler.initACInstance(pubK,0,tK,0,jK1);
		abeHandler.initACInstance(pubK1,1,tK,1,jK2);

		abeHandler.transform_join(0,0,aesCipher);	
		abeHandler.transform_join(1,1,aesCipher1);

	}
}
